package net.qiqbframework.domains;

import net.qiqbframework.commandhandling.CommandHandler;
import net.qiqbframework.common.IdentifierFactory;
import net.qiqbframework.domains.event.application.NoticeCmd;
import net.qiqbframework.domains.event.domain.types.*;
import net.qiqbframework.eventhandling.DomainEventMessage;
import net.qiqbframework.eventhandling.notice.DomainEventChannel;
import net.qiqbframework.eventhandling.notice.DomainEventNoticeResponse;
import net.qiqbframework.modelling.domain.AggregateRoot;
import net.qiqbframework.modelling.domain.EntityIdentifier;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Optional;


@Slf4j
@AggregateRoot
@Getter
@Setter
public class DomainEvent {

    @EntityIdentifier
    private String id;

    protected DomainEventMessage<?> domainEventInfo;
    /**
     * 通知状态
     */
    protected NoticeStatus noticeStatus;
    /**
     * 处理状态
     */
    protected HandlerProcessStatus processStatus;


    private List<InstanceSubscription> subscriptions;

    private List<Feedback> feedbacks;


    private transient DomainEventChannel domainEventChannel;

    public void setDomainEventChannel(DomainEventChannel domainEventChannel) {
        if (domainEventChannel == null) {
            log.error("domainEventChannel is null");
        }
        this.domainEventChannel = domainEventChannel;
    }

    @CommandHandler
    public void notice(NoticeCmd noticeCmd) {

    }


    public void sending() {
        // 通知给其他领域
        final DomainEventNoticeResponse noticeResult = domainEventChannel.notice(domainEventInfo);
        for (DomainEventNoticeResponse.ApplicationSubscriptionDto applicationResult : noticeResult.getApplicationSubscriptions()) {

            final Optional<InstanceSubscription> matchedSubscriptionOptional = this.subscriptions.stream()
                    .filter(s -> s.getApplicationName().equals(applicationResult.getApplicationName())
                            && s.getInstanceName().equals(applicationResult.getInstanceName())).findAny();
            final int processListenerQty = applicationResult.getEventRecord().size();
            if (matchedSubscriptionOptional.isPresent()) {
                matchedSubscriptionOptional.get().setProcessListenerQty(processListenerQty);
            } else {
                final InstanceSubscription subscription = new InstanceSubscription(IdentifierFactory.getInstance().generate(),
                        applicationResult.getApplicationName(), applicationResult.getInstanceName());
                subscription.setProcessListenerQty(processListenerQty);
                subscriptions.add(subscription);
            }
            for (DomainEventNoticeResponse.EventRecordDto listenerProcessor : applicationResult.getEventRecord()) {
                final Optional<Feedback> matchedFeedbackOptional = this.feedbacks.stream().filter(f -> f.getProcessId().equals(listenerProcessor.getReplyId())).findAny();
                if (matchedFeedbackOptional.isPresent()) {
                    matchedFeedbackOptional.get().reset();
                } else {
                    final LocalDateTime localDateTime = LocalDateTime.ofInstant(this.domainEventInfo.getTimestamp(), ZoneId.systemDefault());
                    final Feedback feedback = new Feedback(IdentifierFactory.getInstance().generate(), FeedbackStatus.init, localDateTime);
                    feedback.setProcessId(listenerProcessor.getReplyId());
                    feedback.setApplicationName(applicationResult.getApplicationName());
                    feedback.setInstanceName(applicationResult.getInstanceName());
                    feedback.setProcessListenerId(listenerProcessor.getListenerId());
                    feedbacks.add(feedback);
                }
            }
        }

        // 重置发送状态
        resetNoticeStatus();
        // 重置处理状态
        resetProcessStatus();
    }

    private void resetProcessStatus() {
        if (this.feedbacks.isEmpty()) {
            this.processStatus = HandlerProcessStatus.success;
        } else {
            // 找没有反馈的数据
            final Optional<Feedback> notInitOptional = this.feedbacks.stream().filter(f -> !f.getStatus().equals(FeedbackStatus.init)).findAny();
            if (notInitOptional.isPresent()) {
                final Optional<Feedback> initOptional = this.feedbacks.stream().filter(f -> f.getStatus().equals(FeedbackStatus.init)).findAny();
                if (initOptional.isPresent()) {
                    this.processStatus = HandlerProcessStatus.processing;
                } else {
                    // 是否全部成功
                    final Optional<Feedback> failOptional = this.feedbacks.stream().filter(f -> f.getStatus().equals(FeedbackStatus.fail)).findAny();
                    this.processStatus = failOptional.isPresent() ? HandlerProcessStatus.fail : HandlerProcessStatus.success;
                }
            } else {
                this.processStatus = HandlerProcessStatus.preprocess;
            }

        }
    }

    private void resetNoticeStatus() {
        if (this.subscriptions.isEmpty()) {
            this.noticeStatus = NoticeStatus.ready;
        } else {

            this.noticeStatus = NoticeStatus.sent;
        }
    }

    public void feedback(String processId, boolean processSuccess, String processResultMsg, LocalDateTime processBeginTime, LocalDateTime processEndTime) {
        //
        log.info("开始反馈:{}", processId);
        final Optional<Feedback> matchedOptional = this.feedbacks.stream().filter(f -> f.getProcessId().equals(processId)).findAny();
        if (matchedOptional.isPresent()) {
            log.info("找到反馈:{}", matchedOptional.get().getId());
            Feedback subscription = matchedOptional.get();
            subscription.feedback(processSuccess, processResultMsg, processBeginTime, processEndTime);
        }
        // 重置处理状态
        resetProcessStatus();
    }

    public Integer getFeedbackQty(String subScriptId) {
        int feedbackQty = 0;
        final Optional<InstanceSubscription> matchedOptional = this.subscriptions.stream().filter(s -> s.getId().equals(subScriptId)).findAny();
        if (matchedOptional.isPresent()) {
            final InstanceSubscription domainEventSubscription = matchedOptional.get();

            for (Feedback feedback : this.feedbacks) {
                if (feedback.getApplicationName().equals(domainEventSubscription.getApplicationName())
                        && feedback.getInstanceName().equals(domainEventSubscription.getInstanceName())) {
                    if (feedback.getStatus().equals(FeedbackStatus.init)) {
                        continue;
                    }
                    feedbackQty = feedbackQty + 1;
                }

            }

        }
        return feedbackQty;
    }
}
